package com.hvgroup.modules.dify.service.impl;

import com.hvgroup.modules.dify.service.DifyAppService;
import com.hvgroup.modules.dify.service.dto.AppQueryCriteria;
import com.hvgroup.modules.dify.service.dto.AppsDto;
import com.hvgroup.modules.dify.service.dto.AppsQueryCriteria;
import com.hvgroup.modules.sanic.domain.po.LlmApp;
import com.hvgroup.modules.sanic.domain.po.LlmRolePermission;
import com.hvgroup.modules.sanic.domain.po.QaSession;
import com.hvgroup.modules.sanic.repository.*;
import com.hvgroup.modules.sanic.service.mapstruct.LlmAppMapper;
import com.hvgroup.utils.*;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class DifyAppServiceImpl implements DifyAppService {

    private final QaSessionRepository qaSessionRepository;
    private final LlmModelConfigRepository llmModelConfigRepository;
    private final LlmModelConfigApiRepository llmModelConfigApiRepository;
    private final LlmAppRepository llmAppRepository;
    private final LlmRolePermissionRepository llmRolePermissionRepository;

    private final LlmAppMapper llmAppMapper;

    @Override
    public PageResult<AppsDto> queryAll(AppQueryCriteria criteria, Pageable pageable) {
        Long userId = SecurityUtils.getCurrentUserId();
        List<LlmRolePermission> list = llmRolePermissionRepository.findByUserId(userId);

        List<Long> appIds = list.stream()
                .flatMap(o -> Arrays.stream(StringUtils.defaultString(o.getAppsId(), "").split(",")))
                .map(String::trim)
                .filter(StringUtils::isNotBlank)
                .filter(s -> s.matches("\\d+"))
                .map(Long::valueOf)
                .collect(Collectors.toList());

        criteria.setAppId(appIds);

        Page<LlmApp> page = llmAppRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(llmAppMapper::toAppsDto));
    }

    @Override
    public Map<String, List<AppsDto>> queryGroupApps(AppsQueryCriteria criteria) {
        // TODO
        List<String> appIds = qaSessionRepository.findAllByUserIdOrderByUpdateTimeDesc(Long.valueOf(criteria.getUserId()))
                .stream()
                .map(QaSession::getAppId)
                .collect(Collectors.toList());

        List<LlmApp> use = llmAppRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (ObjectUtils.isNotEmpty(appIds)) {
                CriteriaBuilder.In<String> inClause = cb.in(root.get("appId"));
                appIds.forEach(inClause::value);
                predicates.add(inClause);
            }

            if (StringUtils.isNotEmpty(criteria.getName())) {
                predicates.add(cb.like(root.get("name"), "%" + criteria.getName() + "%"));
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        });
        // 查询后按 appIds 顺序排序
        Map<String, Integer> idOrderMap = new HashMap<>();
        for (int i = 0; i < appIds.size(); i++) {
            idOrderMap.put(appIds.get(i), i);
        }
        use.sort(Comparator.comparingInt(app -> idOrderMap.getOrDefault(app.getId(), Integer.MAX_VALUE)));


        List<LlmApp> oth = llmAppRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (ObjectUtils.isNotEmpty(appIds)) {
                CriteriaBuilder.In<String> inClause = cb.in(root.get("appId"));
                appIds.forEach(inClause::value);
                predicates.add(cb.not(inClause));
            }

            if (StringUtils.isNotEmpty(criteria.getName())) {
                predicates.add(cb.like(root.get("name"), "%" + criteria.getName() + "%"));
            }
//            query.orderBy(cb.desc(root.get("updatedTime")));

            return cb.and(predicates.toArray(new Predicate[0]));
        });


        Map<String, List<AppsDto>> map = new HashMap<>();
        // use  oth
        map.put("use", use.stream().map(llmAppMapper::toAppsDto).collect(Collectors.toList()));
        map.put("oth", oth.stream().map(llmAppMapper::toAppsDto).collect(Collectors.toList()));

        return map;
    }


    @Override
    public Map<String, List<AppsDto>> queryGroupApps2(AppsQueryCriteria criteria) {

        Long userId = SecurityUtils.getCurrentUserId();
        // 得到用户拥有的平台权限
        List<LlmRolePermission> use_list = llmRolePermissionRepository.findByUserId(userId, criteria.getPlatformId());
        // 得到用户拥有的应用
        List<Long> allIds = use_list.stream()
                .flatMap(o -> Arrays.stream(StringUtils.defaultString(o.getAppsId(), "").split(",")))
                .map(String::trim)
                .filter(StringUtils::isNotBlank)
                .filter(s -> s.matches("\\d+"))
                .map(Long::valueOf)
                .collect(Collectors.toList());

        // 没有应用意味着没有权限, 直接返回
        Map<String, List<AppsDto>> map = new HashMap<>();
        if (ObjectUtils.isEmpty(allIds)) {
            map.put("use", Collections.emptyList());
            map.put("oth", Collections.emptyList());
            return map;
        }

        // 已经使用过的应用
        List<String> appIds = qaSessionRepository.findAllByUserIdOrderByUpdateTimeDesc(userId)
                .stream()
                .map(QaSession::getAppId)
                .collect(Collectors.toList());

        List<LlmApp> use = new ArrayList<>();
        if (ObjectUtils.isNotEmpty(appIds)) {
            use = llmAppRepository.findAll((root, query, cb) -> {
                List<Predicate> predicates = new ArrayList<>();

                if (ObjectUtils.isNotEmpty(allIds)) {
                    CriteriaBuilder.In<Long> inClause = cb.in(root.get("id"));
                    allIds.forEach(inClause::value);
                    predicates.add(inClause);
                }

                // 用户使用过的应用
                if (ObjectUtils.isNotEmpty(appIds)) {
                    CriteriaBuilder.In<String> inClause = cb.in(root.get("appId"));
                    appIds.forEach(inClause::value);
                    predicates.add(inClause);
                }
                // 名称过滤(如果有)
                if (StringUtils.isNotEmpty(criteria.getName())) {
                    predicates.add(cb.like(root.get("name"), "%" + criteria.getName() + "%"));
                }
                return cb.and(predicates.toArray(new Predicate[0]));
            });
            // 查询后按 appIds 顺序排序
            Map<String, Integer> idOrderMap = new HashMap<>();
            for (int i = 0; i < appIds.size(); i++) {
                idOrderMap.put(appIds.get(i), i);
            }
            use.sort(Comparator.comparingInt(app -> idOrderMap.getOrDefault(app.getAppId(), Integer.MAX_VALUE)));
        }

        List<LlmApp> oth = llmAppRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 用户拥有的所有应用
            if (ObjectUtils.isNotEmpty(allIds)) {
                CriteriaBuilder.In<Long> inClause = cb.in(root.get("id"));
                allIds.forEach(inClause::value);
                predicates.add(inClause);
            }

            // 未使用过的应用
            if (ObjectUtils.isNotEmpty(appIds)) {
                CriteriaBuilder.In<String> inClause = cb.in(root.get("appId"));
                appIds.forEach(inClause::value);
                predicates.add(cb.not(inClause));
            }

            if (StringUtils.isNotEmpty(criteria.getName())) {
                predicates.add(cb.like(root.get("name"), "%" + criteria.getName() + "%"));
            }
//            query.orderBy(cb.desc(root.get("updatedTime")));

            return cb.and(predicates.toArray(new Predicate[0]));
        });



        // use  oth
        map.put("use", use.stream().map(llmAppMapper::toAppsDto).collect(Collectors.toList()));
        map.put("oth", oth.stream().map(llmAppMapper::toAppsDto).collect(Collectors.toList()));
        return map;
    }

    @Override
    public List<Map<String, String>> queryPlatform() {
        return llmModelConfigRepository.findAll()
                .stream()
                .map(lmc -> {
                    Map<String, String> map = new HashMap<>();
                    map.put("key", String.valueOf(lmc.getId()));
                    map.put("value", lmc.getPlatformName());
                    return map;
                }).collect(Collectors.toList());
    }

}
